M=require("ming_node");
/**
 * serialport@10.4.0
 */
const { SerialPort } = require('serialport')


class CrcUtils{
    static  _auchCRCHi = [
        0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
        0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
        0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
        0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
        0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
        0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
        0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
        0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
        0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
        0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
        0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
        0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
        0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
        0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
        0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
        0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
        0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
        0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
        0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
        0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
        0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
        0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
        0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
        0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
        0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
        0x80, 0x41, 0x00, 0xC1, 0x81, 0x40
    ];
    static _auchCRCLo = [
        0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06,
        0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD,
        0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,
        0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A,
        0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4,
        0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
        0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3,
        0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4,
        0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,
        0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29,
        0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED,
        0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
        0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60,
        0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67,
        0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,
        0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68,
        0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E,
        0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
        0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71,
        0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92,
        0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,
        0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B,
        0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B,
        0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
        0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42,
        0x43, 0x83, 0x41, 0x81, 0x80, 0x40
    ];

    static CRC16Str = function (buffer) {
        var hi = 0xff;
        var lo = 0xff;
        for (var i = 0; i < buffer.length; i++) {
            var idx = hi ^ buffer[i];
            hi = (lo ^ CrcUtils._auchCRCHi[idx]);
            lo = CrcUtils._auchCRCLo[idx];
        }
        return CrcUtils.padLeft((hi << 8 | lo).toString(16).toUpperCase(), 4, '0');
    };

    static  CRC16 = function (buffer) {
        var hi = 0xff;
        var lo = 0xff;
        for (var i = 0; i < buffer.length; i++) {
            var idx = hi ^ buffer[i];
            hi = (lo ^ CrcUtils._auchCRCHi[idx]);
            lo = CrcUtils._auchCRCLo[idx];
        }
        return [hi,lo];
    };
    static isArray = function (arr) {
        return Object.prototype.toString.call(arr) === '[object Array]';
    };

    static ToCRC16 = function (str) {
        return CrcUtils.CRC16(CrcUtils.isArray(str) ? str : CrcUtils.strToByte(str));
    };

    static ToModbusCRC16 = function (str) {
        return CrcUtils.CRC16(CrcUtils.isArray(str) ? str : CrcUtils.strToHex(str));
    };

    static strToByte = function (str) {
        var tmp = str.split(''), arr = [];
        for (var i = 0, c = tmp.length; i < c; i++) {
            var j = encodeURI(tmp[i]);
            if (j.length == 1) {
                arr.push(j.charCodeAt());
            } else {
                var b = j.split('%');
                for (var m = 1; m < b.length; m++) {
                    arr.push(parseInt('0x' + b[m]));
                }
            }
        }
        return arr;
    };

    static convertChinese = function (str) {
        var tmp = str.split(''), arr = [];
        for (var i = 0, c = tmp.length; i < c; i++) {
            var s = tmp[i].charCodeAt();
            if (s <= 0 || s >= 127) {
                arr.push(s.toString(16));
            }
            else {
                arr.push(tmp[i]);
            }
        }
        return arr;
    };

    static filterChinese = function (str) {
        var tmp = str.split(''), arr = [];
        for (var i = 0, c = tmp.length; i < c; i++) {
            var s = tmp[i].charCodeAt();
            if (s > 0 && s < 127) {
                arr.push(tmp[i]);
            }
        }
        return arr;
    };

    static strToHex = function (hex, isFilterChinese) {
        hex = isFilterChinese ? CrcUtils.filterChinese(hex).join('') : CrcUtils.convertChinese(hex).join('');

        //清除所有空格
        hex = hex.replace(/\s/g, "");
        //若字符个数为奇数，补一个空格
        hex += hex.length % 2 != 0 ? " " : "";

        var c = hex.length / 2, arr = [];
        for (var i = 0; i < c; i++) {
            arr.push(parseInt(hex.substr(i * 2, 2), 16));
        }
        return arr;
    };

    static padLeft = function (s, w, pc) {
        if (pc == undefined) {
            pc = '0';
        }
        for (var i = 0, c = w - s.length; i < c; i++) {
            s = pc + s;
        }
        return s;
    };
}

class ModbusRtuRequest{
    reqBuffer;//原始的请求数组
    slaveAddress; //从机地址
    funcCode;
    startAddress;
    len; //读取数量
    status=0;
    totalByte; //总字节数量
}
class MB_ENUM{
    static MB_ADDRESS_BROADCAST =   ( 0 )   /*! Modbus broadcast address. */
    static MB_ADDRESS_MIN =          ( 1 )   /*! Smallest possible slave address. */
    static MB_ADDRESS_MAX =          ( 247 ) /*! Biggest possible slave address. */
    static MB_FUNC_NONE  =                         (  0 )
    static MB_FUNC_READ_COILS  =                   (  1 )
    static MB_FUNC_READ_DISCRETE_INPUTS=           (  2 )
    static MB_FUNC_WRITE_SINGLE_COIL =             (  5 )
    static MB_FUNC_WRITE_MULTIPLE_COILS =          ( 15 )
    static MB_FUNC_READ_HOLDING_REGISTER =         (  3 )
    static MB_FUNC_READ_INPUT_REGISTER  =          (  4 )
    static MB_FUNC_WRITE_REGISTER    =             (  6 )
    static MB_FUNC_WRITE_MULTIPLE_REGISTERS =      ( 16 )
    static MB_FUNC_READWRITE_MULTIPLE_REGISTERS=   ( 23 )
    static MB_FUNC_DIAG_READ_EXCEPTION =           (  7 )
    static MB_FUNC_DIAG_DIAGNOSTIC  =              (  8 )
    static MB_FUNC_DIAG_GET_COM_EVENT_CNT =        ( 11 )
    static MB_FUNC_DIAG_GET_COM_EVENT_LOG =        ( 12 )
    static MB_FUNC_OTHER_REPORT_SLAVEID  =         ( 17 )
    static MB_FUNC_ERROR =                         ( 128 )
}


class ModBusRtuSlaverUtils {

    //请求检查和转换
    static modbusRequestCheckAndConvert(reqBuffer) {
        const mReq=new ModbusRtuRequest();
        mReq.reqBuffer=reqBuffer;
        if(reqBuffer==null){
            mReq.status="CRC_ERR";
            return mReq;
        }
        mReq.totalByte=reqBuffer.length;
        //crc检查
        let crc = CrcUtils.CRC16(reqBuffer);
        if(crc[1]!=0||crc[0]!=0){
            mReq.status="CRC_ERR";
            return mReq;
        }
        mReq.slaveAddress = reqBuffer[0];
        mReq.funcCode = reqBuffer[1];
        mReq.startAddress = reqBuffer[2] * 256 + reqBuffer[3];
        mReq.len = reqBuffer[4] * 256 + reqBuffer[5];

        //数据地址检查,
        switch (mReq.funcCode)
        {
            //读线圈状态
            case MB_ENUM.MB_FUNC_READ_COILS:
            //写线圈
            case MB_ENUM.MB_FUNC_WRITE_SINGLE_COIL:
            //读保持寄存器
            case MB_ENUM.MB_FUNC_READ_HOLDING_REGISTER:
            //写多个保持寄存器
            case MB_ENUM.MB_FUNC_WRITE_MULTIPLE_REGISTERS:
            //读输入寄存器
            case MB_ENUM.MB_FUNC_READ_INPUT_REGISTER:
        }

        return mReq;
    }

    //读线圈
    static async  modbus_fun01(mReq)
    {
        let resBuffer=[];
        let  startAddress = mReq.startAddress;
        let  len = mReq.len;
        resBuffer[0] = mReq.slaveAddress;
        resBuffer[1] = mReq.funcCode;
        //计算返回的字节长度
        let byteLen = 0;
        if (len % 8 == 0)
        {
            byteLen =Number.parseInt( len/8);
        }
        else
        {
            byteLen =Number.parseInt(len/8)  + 1;
        }
        //返回字节数量
        resBuffer[2] = byteLen;
        for (let i = 0; i < byteLen; i++)
        {
            resBuffer[3 + i] = await ModBusRtuSlavePlugin.read(startAddress+i);
        }
        let crc = CrcUtils.CRC16(resBuffer);
        resBuffer[3 + byteLen] = crc[0];
        resBuffer[3 + byteLen + 1] =  crc[1];
        return resBuffer;
    }

    //读取保持寄存器
    static async modbus_fun03(mReq)
    {
        let resBuffer=[];
        let  startAddress = mReq.startAddress;
        let  regLenth = mReq.len;
        let inx = 0;
        resBuffer[inx++] = mReq.slaveAddress;
        resBuffer[inx++] = mReq.funcCode;
        let byteLen = regLenth * 2;
        resBuffer[inx++] =0xff &  byteLen;
        let v=await ModBusRtuSlavePlugin.read(startAddress,regLenth);
        if(v.length!=regLenth){
            return resBuffer;
        }
        for (let i = 0 ; i <  regLenth ; i++)
        {
            resBuffer[inx++] = 0xff & v[i] >> 8;
            resBuffer[inx++] = 0xff & v[i];
        }
        let crc = CrcUtils.CRC16(resBuffer);
        resBuffer[inx++] = crc[0];
        resBuffer[inx++] =  crc[1];
        return resBuffer;
    }


    //读取输入寄存器
    static  async modbus_fun04(mReq)
    {
        let resBuffer=[];
        let  startAddress = mReq.startAddress;
        let  regLenth = mReq.len;
        let inx = 0;
        resBuffer[inx++] = mReq.slaveAddress;
        resBuffer[inx++] = mReq.funcCode;
        let byteLen = regLenth * 2;
        resBuffer[inx++] =0xff &  byteLen;
        for (let j = startAddress ; j < startAddress  + regLenth ; j++)
        {
            let v=await ModBusRtuSlavePlugin.read(j)
            resBuffer[inx++] = 0xff & v >> 8;
            resBuffer[inx++] = 0xff & v;
        }
        let crc = CrcUtils.CRC16(resBuffer);
        resBuffer[inx++] = crc[0];
        resBuffer[inx++] =  crc[1];
        return resBuffer;
    }


    //写单个线圈
    static async  modbus_fun05(mReq)
    {
        let resBuffer=[];
        let  startAddress = mReq.startAddress;
        //写的入线圈值
        let writeValue = mReq.len;
        for (let i = 0; i < mReq.totalByte; i++)
        {
            resBuffer[i] = reqBuffer[i];
        }
        //第4个字节是FF说明是置位
        if (reqBuffer[4] == 0xff)
        {
            ModBusRtuSlavePlugin.write(startAddress,1);
        }
        else
        {
            ModBusRtuSlavePlugin.write(startAddress,0);
        }
        return resBuffer;
    }

    //写多个保持寄存器
    static async  modbus_fun16(mReq)
    {
        let resBuffer=[];
        let  startAddress = mReq.startAddress;
        //写入的寄存器数量
        let registNum = mReq.len;
        let inx = 0;
        // u8 *resDataBuffer = modbusData.coil.buffer;
        for (let i = 0; i < 4; i++)
        {
            resBuffer[inx++] = mReq.reqBuffer[i];
        }
        resBuffer[inx++] = mReq.reqBuffer[4];
        resBuffer[inx++] = mReq.reqBuffer[5];
        let crc = CrcUtils.CRC16(resBuffer);
        resBuffer[inx++] = crc[0];
        resBuffer[inx++] =  crc[1];
        let wordBuf=[];
        for (let j = 0 ; j <  registNum ; j++)
        {
            let v=mReq.reqBuffer[7+j*2]*256+mReq.reqBuffer[7+j*2+1];
            wordBuf.push(v)
        }
        ModBusRtuSlavePlugin.write(startAddress,wordBuf);
        return resBuffer;
    }

    //写单个保持寄存器
    static async  modbus_fun06(mReq)
    {
        await ModBusRtuSlavePlugin.write(mReq.startAddress,[mReq.len]);
        return mReq.reqBuffer;
    }


    /**
     * 拆分
     * @param mReq
     * @returns {[]|number[]}
     */
    static async modbusDeal(mReq){
        switch (mReq.funcCode){
            //读线圈状态
            case MB_ENUM.MB_FUNC_READ_COILS:
            {
                return ModBusRtuSlaverUtils.modbus_fun01(mReq);
            }
            //读保持寄存器
            case MB_ENUM.MB_FUNC_READ_HOLDING_REGISTER:
            {
                return ModBusRtuSlaverUtils.modbus_fun03(mReq);
            }
            //读输入寄存器
            case MB_ENUM.MB_FUNC_READ_INPUT_REGISTER:
            {
                return ModBusRtuSlaverUtils.modbus_fun04(mReq);
            }
            //写单个线圈
            case MB_ENUM.MB_FUNC_WRITE_SINGLE_COIL:
            {
                return ModBusRtuSlaverUtils.modbus_fun05(mReq);
            }
            //写单个保持寄存器
            case MB_ENUM.MB_FUNC_WRITE_REGISTER:
            {
                return ModBusRtuSlaverUtils.modbus_fun06(mReq);
            }
            //写多个保持寄存器
            case MB_ENUM.MB_FUNC_WRITE_MULTIPLE_REGISTERS:
            {
                return ModBusRtuSlaverUtils.modbus_fun16(mReq);
            }
                return [0];
        }
    }

}







class ModBusRtuSlavePlugin {
    static isLogRecBuf=false;
    static getV=(addr)=>{return 0}
    static setV=(addr,v)=>{return 0}

    constructor({com,brand,read,write}) {
        this.sp = new SerialPort({ path: com, baudRate: brand });
        ModBusRtuSlavePlugin.read=read;
        ModBusRtuSlavePlugin.write=write;
        this.init();
        console.log("listen on "+com)
    }

    static callResolve=null;


    /**
     * 读取1到2000离散输入的状态
     * @param slaveAddress
     * @param startAddress
     * @param numberOfPoints
     * @returns {Promise<unknown>}
     */
    async readInputs(slaveAddress,  startAddress,  numberOfPoints){
        let r=  [];
        this.sp.write(new Uint8Array(r));
        return new Promise((resolve, reject) => {
            setTimeout(() => resolve("timeOut"), 1000);
            ModBusRtuSlavePlugin.callResolve=resolve;
        })
    }


    install(app,args){

    }


    init(){
        // 以 paused mode 监听收到的数据，需要主动读取数据
        this.sp.on('readable', async () => {
            await M.delayMs(5);
            let reqBuffer=  this.sp.read();
            if(reqBuffer==null || reqBuffer.length<5){
                return [];
            }
            let mReq =await ModBusRtuSlaverUtils.modbusRequestCheckAndConvert(reqBuffer);
            if(mReq.status==0){
                let r=await ModBusRtuSlaverUtils.modbusDeal(mReq);
                this.sp.write(new Uint8Array(r));
            }else {
                console.error(mReq.status)
            }
        });


        this.sp.on('data', (data) => {
            if(ModBusRtuSlavePlugin.isLogRecBuf){
                console.log(data);
            }
        });

    }

}

module.exports = ModBusRtuSlavePlugin;